 /*******************************************************************************
  * Copyright (c) 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  ******************************************************************************/

 package org.eclipse.ui.views.markers.internal;

 import java.util.ArrayList ;
 import java.util.Collection ;
 import java.util.HashMap ;
 import java.util.HashSet ;
 import java.util.Iterator ;
 import java.util.Map ;

 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.swt.graphics.Image;

 /**
  * @since 3.2
  *
  */
 public class FieldMarkerGroup implements IField {

     class EntryMapping {
         MarkerGroupingEntry groupingEntry;

         /**
          * Create an entry mapping for the receiver.
          *
          * @param entry
          */
         EntryMapping(MarkerGroupingEntry entry) {
             groupingEntry = entry;
         }

         /**
          * Return whether or not the receiver tests attributes.
          *
          * @return boolean
          */
         public boolean hasAttributes() {
             return false;
         }

         /**
          * Test the attribute of the marker to find a grouping.
          *
          * @param marker
          * @return MarkerGroupingEntry or <code>null</code> if there is not
          * entry.
          */
         public MarkerGroupingEntry testAttribute(ConcreteMarker marker) {
             return null;
         }
     }

     private static MarkerGroupingEntry undefinedEntry = new MarkerGroupingEntry(
             MarkerMessages.FieldCategory_Uncategorized, null, 0);

     class AttributeMapping extends EntryMapping {

         String attribute;

         String attributeValue;

         /**
          * Create a mapping for an attribute with name attributeName and value
          * value to the supplied entry.
          *
          * @param entry
          * @param attributeName
          * @param value
          */
         AttributeMapping(MarkerGroupingEntry entry, String attributeName,
                 String value) {
             super(entry);
             attribute = attributeName;
             attributeValue = value;
         }

         /*
          * (non-Javadoc)
          *
          * @see org.eclipse.ui.views.markers.internal.FieldMarkerGroup.EntryMapping#hasAttributes()
          */
         public boolean hasAttributes() {
             return true;
         }

         /*
          * (non-Javadoc)
          *
          * @see org.eclipse.ui.views.markers.internal.FieldMarkerGroup.EntryMapping#testAttribute(org.eclipse.ui.views.markers.internal.ConcreteMarker)
          */
         public MarkerGroupingEntry testAttribute(ConcreteMarker marker) {
             Object value;
             
             if(!marker.getMarker().exists())
                 return null;//If the marker was deleted during the update drop it

             try {
                 value = marker.getMarker().getAttribute(attribute);
             } catch (CoreException e) {
                 Util.log(e);
                 return null;
             }
             
             if (value != null && attributeValue.equals(value.toString())) {
                 return groupingEntry;
             }
             return null;
         }
     }

     private String title;

     private String id;

     private Map typesToMappings = new HashMap ();

     private boolean showing;

     /**
      * Create a new instance of the receiver called name with id identifier.
      *
      * @param name
      * @param identifier
      */
     public FieldMarkerGroup(String name, String identifier) {
         title = name;
         id = identifier;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.views.markers.internal.IField#getDescription()
      */
     public String getDescription() {
         return title;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.views.markers.internal.IField#getDescriptionImage()
      */
     public Image getDescriptionImage() {
         return null;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.views.markers.internal.IField#getColumnHeaderText()
      */
     public String getColumnHeaderText() {
         return title;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.views.markers.internal.IField#getColumnHeaderImage()
      */
     public Image getColumnHeaderImage() {
         return null;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.views.markers.internal.IField#getValue(java.lang.Object)
      */
     public String getValue(Object obj) {
         MarkerNode node = (MarkerNode) obj;

         if (node.isConcrete()) {
             MarkerGroupingEntry groupingEntry = getMapping((ConcreteMarker) node);
             return groupingEntry.getLabel();
         }
         return node.getDescription();
     }

     /**
      * Get the attribute mapping for the marker
      *
      * @param marker
      * @return MarkerGroupingEntry
      */
     private MarkerGroupingEntry getMapping(ConcreteMarker marker) {

         if (marker.getGroup() == null) {
             marker.setGroup(findGroupValue(marker));
         }
         return (MarkerGroupingEntry) marker.getGroup();
     }

     /**
      * Find the group value. If it cannot be found in an attribute mapping then
      * return null;
      *
      * @param marker
      * @return String or <code>null</code>
      */
     private MarkerGroupingEntry findGroupValue(ConcreteMarker marker) {

         if (typesToMappings.containsKey(marker.getType())) {
             EntryMapping defaultMapping = null;
             Iterator mappings = ((Collection ) typesToMappings.get(marker
                     .getType())).iterator();
             while (mappings.hasNext()) {
                 EntryMapping mapping = (EntryMapping) mappings.next();
                 if (mapping.hasAttributes()) {
                     MarkerGroupingEntry entry = mapping.testAttribute(marker);
                     if (entry != null) {
                         return entry;
                     }
                 } else {
                     // If it has no attributes it is our default
 defaultMapping = mapping;
                 }
             }
             if (defaultMapping != null) {
                 return defaultMapping.groupingEntry;
             }

         }

         return undefinedEntry;

     } /*
          * (non-Javadoc)
          *
          * @see org.eclipse.ui.views.markers.internal.IField#getImage(java.lang.Object)
          */

     public Image getImage(Object obj) {
         return null;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.views.markers.internal.IField#compare(java.lang.Object,
      * java.lang.Object)
      */
     public int compare(Object obj1, Object obj2) {

         MarkerGroupingEntry entry1 = getMapping(((MarkerNode) obj1)
                 .getConcreteRepresentative());
         MarkerGroupingEntry entry2 = getMapping(((MarkerNode) obj2)
                 .getConcreteRepresentative());
         return entry2.getPriority() - entry1.getPriority();

     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.views.markers.internal.IField#getDefaultDirection()
      */
     public int getDefaultDirection() {
         return TableComparator.ASCENDING;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.views.markers.internal.IField#getPreferredWidth()
      */
     public int getPreferredWidth() {
         return 75;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.views.markers.internal.IField#isShowing()
      */
     public boolean isShowing() {
         return showing;
     }

     /*
      * (non-Javadoc)
      *
      * @see org.eclipse.ui.views.markers.internal.IField#setShowing(boolean)
      */
     public void setShowing(boolean showing) {
         this.showing = showing;

     }

     /**
      * Set entry and the default entry for the supplied markerType.
      *
      * @param markerType
      * @param entry
      */

     public void setAsDefault(String markerType, MarkerGroupingEntry entry) {
         addEntry(markerType, new EntryMapping(entry));

     }

     /**
      * Add the entry for the markerType.
      *
      * @param markerType
      * @param entry
      */
     private void addEntry(String markerType, EntryMapping entry) {

         MarkerType[] allDerived = getMarkerTypes(markerType);

         for (int i = 0; i < allDerived.length; i++) {
             Collection entries = new HashSet ();
             MarkerType type = allDerived[i];
             if (typesToMappings.containsKey(type.getId())) {
                 entries = (Collection ) typesToMappings.get(markerType);
             } else {
                 entries = new HashSet ();
             }

             entries.add(entry);
             typesToMappings.put(type.getId(), entries);
         }

     }

     /**
      * Return the marker types that match and are subtypes of markerType.
      * @param markerType
      * @return MarkerType[]
      */
     private MarkerType[] getMarkerTypes(String markerType) {
         MarkerTypesModel model = MarkerTypesModel.getInstance();
         Collection types = new HashSet ();

         MarkerType type = model.getType(markerType);
         if (type != null) {
             types.add(type);
             MarkerType[] subs = type.getAllSubTypes();
             for (int j = 0; j < subs.length; j++) {
                 types.add(subs[j]);
             }
         }

         if (types.isEmpty()) {
             return new MarkerType[0];
         }

         MarkerType[] typesArray = new MarkerType[types.size()];
         types.toArray(typesArray);
         return typesArray;
     }

     /**
      * Add an attributeMapping for the markerType.
      *
      * @param markerType
      * @param attribute
      * @param attributeValue
      * @param entry
      */
     public void mapAttribute(String markerType, String attribute,
             String attributeValue, MarkerGroupingEntry entry) {
         addEntry(markerType, new AttributeMapping(entry, attribute,
                 attributeValue));

     }

     /**
      * Return the id of the receiver.
      *
      * @return String
      */
     public String getId() {
         return id;
     }

     /**
      * Remove the entry from all of the entries in the receiver.
      * @param entry
      */
     public void remove(MarkerGroupingEntry entry) {
         Iterator entries = typesToMappings.values().iterator();
         Collection removeCollection = new ArrayList ();
         while(entries.hasNext()){
             Collection mappings = (Collection ) entries.next();
             Iterator mappingsIterator = mappings.iterator();
             while(mappingsIterator.hasNext()){
                 EntryMapping next = (EntryMapping) mappingsIterator.next();
                 if(next.groupingEntry.equals(entry)){
                     removeCollection.add(next);
                 }
                     
             }
             mappings.removeAll(removeCollection);
             removeCollection.clear();
         }
         
     }
 }

